package cap

import (
	"context"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"fmt"
	"image/png"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// CaptchaType 定义验证码类型
type CaptchaType int

const (
	// NumberCaptcha 纯数字验证码
	NumberCaptcha CaptchaType = iota
	// AlphanumericCaptcha 数字字母组合验证码
	AlphanumericCaptcha
	// MathExpressionCaptcha 数学表达式验证码
	MathExpressionCaptcha
)

// CaptchaResult 存储验证码结果信息
type CaptchaResult struct {
	ID       string // 验证码唯一标识
	Text     string // 验证码文本
	Answer   string // 验证码答案（用于数学计算类型）
	Type     CaptchaType // 验证码类型
	FilePath string // 验证码图片保存路径
}

// CaptchaManager 验证码管理器，提供上层封装功能
type CaptchaManager struct {
	captcha       *Captcha
	storage       CaptchaStorage
	expiration    time.Duration
	defaultCtx    context.Context
}

// NewCaptchaManager 创建默认的验证码管理器实例（使用内存存储）
func NewCaptchaManager() *CaptchaManager {
	return &CaptchaManager{
		captcha:    NewCaptcha(),
		storage:    NewMemoryStorage(),
		expiration: 5 * time.Minute, // 默认5分钟过期
		defaultCtx: context.Background(),
	}
}

// SetCaptchaOptions 设置验证码基础选项
func (cm *CaptchaManager) SetCaptchaOptions(width, height int, fontSize float64) {
	cm.captcha.Width = width
	cm.captcha.Height = height
	cm.captcha.FontSize = fontSize
}

// SetColors 设置验证码颜色
func (cm *CaptchaManager) SetColors(bgR, bgG, bgB, bgA, textR, textG, textB, textA uint8) {
	cm.captcha.SetBgColor(bgR, bgG, bgB, bgA)
	cm.captcha.SetTextColor(textR, textG, textB, textA)
}

// SetFontDir 设置字体目录
func (cm *CaptchaManager) SetFontDir(fontDir string) {
	// 检查目录是否存在，如果不存在则使用默认目录
	if _, err := os.Stat(fontDir); os.IsNotExist(err) {
		// 使用fontloader中的智能字体目录查找
		fontDir = GetDefaultFontDir()
	}
	// 设置captcha的字体目录
	cm.captcha.SetFontDir(fontDir)
}

// 生成并存储验证码的通用方法
func (cm *CaptchaManager) generateAndStoreCaptcha(text, answer string, captchaType CaptchaType) (CaptchaResult, error) {
	// 创建唯一ID
	id := generateUniqueID()

	// 存储验证码结果
	result := CaptchaResult{
		ID:       id,
		Text:     text,
		Answer:   answer,
		Type:     captchaType,
		FilePath: "", // 默认不设置文件路径
	}

	// 保存到存储
	if err := cm.storage.Set(cm.defaultCtx, id, result, cm.expiration); err != nil {
		return CaptchaResult{}, fmt.Errorf("存储验证码失败: %w", err)
	}

	return result, nil
}

// GenerateNumberCaptcha 生成纯数字验证码
func (cm *CaptchaManager) GenerateNumberCaptcha(length int) (CaptchaResult, error) {
	if length <= 0 {
		return CaptchaResult{}, errors.New("验证码长度必须大于0")
	}

	// 生成随机数字
	text := generateRandomNumbers(length)

	// 使用通用方法生成并存储验证码
	return cm.generateAndStoreCaptcha(text, text, NumberCaptcha)
}

// GenerateAlphanumericCaptcha 生成数字字母组合验证码
func (cm *CaptchaManager) GenerateAlphanumericCaptcha(length int) (CaptchaResult, error) {
	if length <= 0 {
		return CaptchaResult{}, errors.New("验证码长度必须大于0")
	}

	// 生成随机字母和数字
	text := generateRandomAlphanumeric(length)

	// 使用通用方法生成并存储验证码
	return cm.generateAndStoreCaptcha(text, text, AlphanumericCaptcha)
}

// GenerateMathExpressionCaptcha 生成数学表达式验证码
func (cm *CaptchaManager) GenerateMathExpressionCaptcha() (CaptchaResult, error) {
	// 生成数学表达式和答案
	expression, answer := generateMathExpression()

	// 使用通用方法生成并存储验证码
	return cm.generateAndStoreCaptcha(expression, answer, MathExpressionCaptcha)
}

// VerifyCaptcha 验证验证码
func (cm *CaptchaManager) VerifyCaptcha(id, userInput string) bool {
	// 查找验证码，支持重试
	const maxRetries = 2
	var result CaptchaResult
	var err error
	
	for attempt := 0; attempt <= maxRetries; attempt++ {
		result, err = cm.storage.Get(cm.defaultCtx, id)
		if err == nil {
			break // 成功获取验证码
		}
		
		// 验证码不存在/过期，直接返回false
		if errors.Is(err, ErrCaptchaNotFound) {
			return false
		}
		
		// 其他错误，重试
		if attempt < maxRetries {
			time.Sleep(10 * time.Millisecond)
		} else {
			// 所有重试都失败
			return false
		}
	}

	// 根据验证码类型进行验证
	// 对于数学表达式，需要比较用户输入的答案与正确答案
	if result.Type == MathExpressionCaptcha {
		return strings.TrimSpace(userInput) == result.Answer
	}

	// 对于其他类型，直接比较文本
	return strings.EqualFold(strings.TrimSpace(userInput), result.Answer)
}

// RemoveCaptcha 移除已验证的验证码
func (cm *CaptchaManager) RemoveCaptcha(id string) {
	cm.storage.Delete(cm.defaultCtx, id)
}

// SetStorage 设置验证码存储实现
func (cm *CaptchaManager) SetStorage(storage CaptchaStorage) {
	cm.storage = storage
}

// SetExpiration 设置验证码过期时间
func (cm *CaptchaManager) SetExpiration(expiration time.Duration) {
	cm.expiration = expiration
}

// SetContext 设置默认上下文
func (cm *CaptchaManager) SetContext(ctx context.Context) {
	cm.defaultCtx = ctx
}

// NewCaptchaManagerWithRedis 创建使用Redis存储的验证码管理器实例
func NewCaptchaManagerWithRedis(options RedisOptions) *CaptchaManager {
	client := NewRedisClient(options)
	return &CaptchaManager{
		captcha:    NewCaptcha(),
		storage:    NewRedisStorage(client),
		expiration: 5 * time.Minute,
		defaultCtx: context.Background(),
	}
}

// 生成随机数字
func generateRandomNumbers(length int) string {
	const digits = "0123456789"
	result := make([]byte, length)
	for i := range result {
		// 使用crypto/rand生成更安全的随机数
		idx, _ := rand.Int(rand.Reader, big.NewInt(int64(len(digits))))
		result[i] = digits[idx.Int64()]
	}
	return string(result)
}

// 生成随机字母和数字组合
func generateRandomAlphanumeric(length int) string {
	const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
	result := make([]byte, length)
	for i := range result {
		// 使用crypto/rand生成更安全的随机数
		idx, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		result[i] = charset[idx.Int64()]
	}
	return string(result)
}

// 生成数学表达式和答案
func generateMathExpression() (string, string) {
	operators := []string{"+", "-", "*"}
	// 使用crypto/rand生成更安全的随机数
	opIdx, _ := rand.Int(rand.Reader, big.NewInt(int64(len(operators))))
	op := operators[opIdx.Int64()]

	var a, b int
	var result int

	// 根据运算符设置合适的数值范围
	switch op {
	case "+":
		a = 1 + randIntn(9)
		b = 1 + randIntn(9)
		result = a + b
	case "-":
		// 确保结果为正数
		b = 1 + randIntn(9)
		a = b + 1 + randIntn(9)
		result = a - b
	case "*":
		// 乘法使用较小的数字以避免结果过大
		a = 1 + randIntn(5)
		b = 1 + randIntn(5)
		result = a * b
	}

	// 格式为 "数字运算符数字=?"
	expression := fmt.Sprintf("%d%s%d=?", a, op, b)
	answer := strconv.Itoa(result)

	return expression, answer
}

// 生成唯一ID
func generateUniqueID() string {
	// 生成一个随机数用于ID的一部分
	randomPart := randInt63()
	return fmt.Sprintf("captcha_%d_%d", time.Now().UnixNano(), randomPart)
}

// randIntn 生成[0, max)范围的随机整数
func randIntn(max int) int {
	if max <= 0 {
		return 0
	}
	// 使用crypto/rand生成更安全的随机数
	val, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
	return int(val.Int64())
}

// randInt63 生成随机的64位整数
func randInt63() int64 {
	var buf [8]byte
	_, err := rand.Read(buf[:])
	if err != nil {
		// 出错时回退到时间戳生成
		return time.Now().UnixNano()
	}
	return int64(binary.BigEndian.Uint64(buf[:]))
}

// SaveCaptchaImage 保存验证码图片
func (cm *CaptchaManager) SaveCaptchaImage(id, text, outputDir string) (string, error) {
	// 确保输出目录存在
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return "", fmt.Errorf("创建输出目录失败: %w", err)
	}

	// 生成文件路径
	fileName := fmt.Sprintf("%s.png", id)
	filePath := filepath.Join(outputDir, fileName)

	// 生成验证码图片
	if err := cm.captcha.Generate(text); err != nil {
		return "", fmt.Errorf("生成验证码图片失败: %w", err)
	}

	// 保存到文件
	if err := cm.captcha.SaveToFile(filePath); err != nil {
		return "", fmt.Errorf("保存验证码图片失败: %w", err)
	}

	return filePath, nil
}

// GetCaptchaBase64 获取验证码的base64编码
func (cm *CaptchaManager) GetCaptchaBase64(id string) (string, error) {
	// 查找验证码，支持重试
	const maxRetries = 2
	var result CaptchaResult
	var err error
	
	for attempt := 0; attempt <= maxRetries; attempt++ {
		result, err = cm.storage.Get(cm.defaultCtx, id)
		if err == nil {
			break // 成功获取验证码
		}
		
		// 如果不是验证码不存在/过期错误，直接返回
		if !errors.Is(err, ErrCaptchaNotFound) {
			return "", fmt.Errorf("获取验证码失败: %w", err)
		}
		
		// 重试时等待一小段时间
		if attempt < maxRetries {
			time.Sleep(10 * time.Millisecond)
		}
	}
	
	// 所有重试都失败
	if err != nil {
		return "", fmt.Errorf("获取验证码失败: %w", err)
	}

	// 重新生成验证码图像
	if err := cm.captcha.Generate(result.Text); err != nil {
		return "", fmt.Errorf("生成验证码图片失败: %w", err)
	}

	// 转换为base64
	base64Str, err := cm.captcha.ToBase64()
	if err != nil {
		return "", fmt.Errorf("转换为base64失败: %w", err)
	}

	return base64Str, nil
}

// ServeCaptchaImage 提供验证码图片服务
func (cm *CaptchaManager) ServeCaptchaImage(w http.ResponseWriter, r *http.Request, id string) error {
	// 查找验证码，支持重试
	const maxRetries = 2
	var result CaptchaResult
	var err error
	
	for attempt := 0; attempt <= maxRetries; attempt++ {
		result, err = cm.storage.Get(cm.defaultCtx, id)
		if err == nil {
			break // 成功获取验证码
		}
		
		// 如果不是验证码不存在/过期错误，直接返回
		if !errors.Is(err, ErrCaptchaNotFound) {
			return fmt.Errorf("获取验证码失败: %w", err)
		}
		
		// 重试时等待一小段时间
		if attempt < maxRetries {
			time.Sleep(10 * time.Millisecond)
		}
	}
	
	// 所有重试都失败
	if err != nil {
		return fmt.Errorf("获取验证码失败: %w", err)
	}

	// 重新生成验证码图像
	if err := cm.captcha.Generate(result.Text); err != nil {
		return fmt.Errorf("生成验证码图片失败: %w", err)
	}

	// 设置验证码ID
	cm.captcha.SetCaptchaID(id)

	// 设置响应头
	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
	w.Header().Set("Pragma", "no-cache")
    
    // 如果设置了验证码ID，则在响应头中返回
    if cm.captcha.CaptchaID != "" {
        w.Header().Set(cm.captcha.CaptchaIDHeader, cm.captcha.CaptchaID)
    }

	// 将图片编码为PNG并写入响应
	if err := png.Encode(w, cm.captcha.GetImage()); err != nil {
		return fmt.Errorf("编码验证码图片失败: %w", err)
	}

	return nil
}

// SetCaptchaIDHeader 设置响应头中验证码ID的名称
func (cm *CaptchaManager) SetCaptchaIDHeader(headerName string) {
	if headerName != "" {
		cm.captcha.SetCaptchaIDHeader(headerName)
	}
}